﻿using System;
using System.Reflection;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
using System.Web;
using CMS.Web.Engine;

namespace CMS.Web.Data
{
    public interface IPagesRepository
    {
        IPageEntity GetPage(string path, IHostEntity host);
        IPageEntity GetPage(int ID);
        IPageEntity GetPage(string path, string host, int port);
        IWidget GetWidget(int ID);
        void UpdateWidgetOrders(int page, string zone, List<int> ids);
        void UpdateWidget(IWidget widget);
        void DeleteWidget(int ID);
        void AddWidgets(int page, string zone, Guid[] widgets);
    }
    public class PagesRepository : IPagesRepository
    {        
        private XElement _pages = XElement.Load(HttpContext.Current.Server.MapPath("~/Pages.xml"));

        public IPageEntity GetPage(int ID)
        {
            IPageEntity page = _pages
                .Descendants("page")
                .Where(x => x.AttributeValue<int>("ID") == ID)
                .Select(x => (IPageEntity)new PageEntity
                {
                    ID = x.AttributeValue<int>("ID"),
                    ViewName = x.AttributeValue<string>("viewName"),
                    Host = x.AttributeValue<int>("host"),
                    Text = x.AttributeValue<string>("text"),
                    Aliases = new List<string>(x.AttributeValue<string>("aliases").Split("#")),
                    Model = (IPageModel)new PageModel
                    {
                        ID = x.AttributeValue<int>("ID"),
                        Widgets = x.Elements("widget").Select(y => CreateWidgetInstance
                        (
                            y.AttributeValue<int>("ID"),
                            y.AttributeValue<int>("order"),
                            Type.GetType(y.AttributeValue<string>("type")),
                            y.AttributeValue<string>("zone"),
                            y.AttributeValue<string>("settings"),
                            y.AttributeValue<string>("partialViewName"),
                            null
                        )).ToList()
                    }
                }).SingleOrDefault();

            if (page.IsNull())
                throw new Exception("Страница ID={0} не найдена.".Args(ID));

            return page;
        }

        public IPageEntity GetPage(string path, string hostHeader, int port)
        {
            IHostEntity host = IoCContainer.Current.Resolve<IHostsRepository>().GetHost(hostHeader, port);
            return GetPage(path, host);
        }

        public IPageEntity GetPage(string path, IHostEntity host)
        {           
            string[] pathParts = path.GetValueOrDefault<string>(string.Empty).Split("/");
            string alias = pathParts.Where(x => !x.IsDigitOrEmpty()).Join("/");
            IEnumerable<string> ids = pathParts.Where(x => x.IsDigit());           

            IPageEntity page = _pages
                .Descendants("page")
                .Where(x => x.AttributeValue<int>("host") == host.ID && x.AttributeValue<string>("aliases").Split("#").Contains(alias, new PageAliasEqualityComparer()))
                .Select(x => (IPageEntity)new PageEntity
                {
                    ID = x.AttributeValue<int>("ID"),
                    ViewName = x.AttributeValue<string>("viewName"),
                    Host = host.ID,
                    Text = x.AttributeValue<string>("text"),
                    Aliases = new List<string>(x.AttributeValue<string>("aliases").Split("#")),
                    Model = (IPageModel)new PageModel
                    {
                        ID = x.AttributeValue<int>("ID"),
                        Widgets = x.Elements("widget").Select(y => CreateWidgetInstance
                        (                            
                            y.AttributeValue<int>("ID"),
                            y.AttributeValue<int>("order"),
                            Type.GetType(y.AttributeValue<string>("type")),
                            y.AttributeValue<string>("zone"),
                            y.AttributeValue<string>("settings"),
                            y.AttributeValue<string>("partialViewName"),
                            ids
                        )).ToList()
                    }
                }).SingleOrDefault();          

            if (page.IsNull())
                throw new Exception("Страница \"{0}\" не найдена.".Args(alias));

            return page;
        }

        private IWidget CreateWidgetInstance(int id, int order, Type type, string zone, string settings, string partialViewName, IEnumerable<string> ids) 
        {
            WidgetBase widget = (WidgetBase)Activator.CreateInstance(type);
            widget.ID = id;
            widget.Order = order;
            widget.Zone = zone;
            widget.PartialViewName = !partialViewName.IsEmpty() ? partialViewName : widget.Annotation.PartialViewName;
            widget.Initialize(settings, ids);
            return widget;
        }

        public void UpdateWidget(IWidget widget)
        {
            string settings = Json.Serialize(widget.Settings);
            XElement xElement = _pages.Descendants("widget").SingleOrDefault(x => x.AttributeValue<int>("ID").Equals(widget.ID));
            xElement.AttributeValue("settings", settings);
            _pages.Save(HttpContext.Current.Server.MapPath("~/Pages.xml"));
        }

        public IWidget GetWidget(int ID)
        {
            XElement xElement = _pages.Descendants("widget").SingleOrDefault(x => x.AttributeValue<int>("ID").Equals(ID));
            return CreateWidgetInstance(
                xElement.AttributeValue<int>("ID"),
                xElement.AttributeValue<int>("order"),
                Type.GetType(xElement.AttributeValue<string>("type")),
                xElement.AttributeValue<string>("zone"),
                xElement.AttributeValue<string>("settings"),
                xElement.AttributeValue<string>("partialViewName"),
                null);
        }

        public void UpdateWidgetOrders(int page, string zone, List<int> ids)
        {
            foreach (int id in ids)
            {
                XElement widget = _pages.Descendants("widget").SingleOrDefault(x => x.AttributeValue<int>("ID") == id);
                widget.AttributeValue("order", ids.IndexOf(id));
                widget.AttributeValue("zone", zone);
            }
            _pages.Save(HttpContext.Current.Server.MapPath("~/Pages.xml"));            
        }

        public void AddWidgets(int page, string zone, Guid[] guids)
        {
            XElement pageNode = _pages.Descendants("page").Where(x => x.AttributeValue<int>("ID") == page).FirstOrDefault();          

            foreach (var type in Assembly.GetExecutingAssembly().GetTypes())
            {
                if (!typeof(WidgetBase).IsAssignableFrom(type))
                    continue;

                WidgetBase widget = (WidgetBase)Activator.CreateInstance(type);
                if (guids.Any(x => !widget.Annotation.Guid.IsEmpty() && x.Equals(new Guid(widget.Annotation.Guid))))
                {
                    Random random = new Random();
                    int newID = random.Next(0, 10000);
                    
                    int widgetCountInZone = pageNode.Elements().Where(x => x.AttributeValue<string>("zone") == zone).Count();
                    XElement newWidget = new XElement("widget", new XAttribute("ID", newID), new XAttribute("type", type.FullName), new XAttribute("zone", zone), new XAttribute("settings", Json.Serialize(widget.Settings)), new XAttribute("order", widgetCountInZone));
                    pageNode.Add(newWidget);                    
                }
            }
            _pages.Save(HttpContext.Current.Server.MapPath("~/Pages.xml"));    
        }
        public void DeleteWidget(int ID)
        {
            XElement xElement = _pages.Descendants("widget").SingleOrDefault(x => x.AttributeValue<int>("ID").Equals(ID));
            xElement.Remove();
            _pages.Save(HttpContext.Current.Server.MapPath("~/Pages.xml"));
        }
    }
    public class PageAliasEqualityComparer : IEqualityComparer<string>
    {
        public bool Equals(string x, string y)
        {
            return x.Equals(y, StringComparison.InvariantCultureIgnoreCase);
        }
        public int GetHashCode(string x)
        {
            return x.GetHashCode();
        }
    }
   
    public class PageEntity : IPageEntity
    {
        public int ID { get; set; }
        public int Host { get; set; }
        public string Text { get; set; }
        public string ViewName { get; set; }
        public IEnumerable<string> Aliases { get; set; }
        public IPageModel Model { get; set; }              
    }
    
    public class PageModel : IPageModel
    {
        private List<IWidget> _widgets;

        public int ID { get; set; }
        public List<IWidget> Widgets
        {
            get { return _widgets; }
            set { _widgets = value; }
        }
    }
}